﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace WFExtended.Twitter
{
    /// <summary>
    /// Twitter Request Parameter Enconding Behavior
    /// </summary>
    public class ParameterEncodingBehavior : IParameterInspector, IEndpointBehavior
    {
        #region IParameterInspector

        /// <summary>
        /// Called after client calls are returned and before service responses are sent.
        /// </summary>
        /// <param name="operationName">The name of the invoked operation.</param>
        /// <param name="outputs">Any output objects.</param>
        /// <param name="returnValue">The return value of the operation.</param>
        /// <param name="correlationState">Any correlation state returned from the <see cref="M:System.ServiceModel.Dispatcher.IParameterInspector.BeforeCall(System.String,System.Object[])" /> method, or null.</param>
        public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
        {
            InspectResult(returnValue);
        }

        private void InspectResult(object result)
        {
            if (result == null)
                return;

            if (result is IEnumerable)
                foreach (var item in (result as IEnumerable))
                    InspectResult(item);
            else
            {
                foreach (var property in result.GetType().GetProperties())
                {
                    if (property.PropertyType == typeof(String))
                    {
                        object value = property.GetValue(result);
                        if(value != null)
                            property.SetValue(result, HttpUtility.UrlDecode(value.ToString()));
                    }
                    else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
                        InspectResult(property.GetValue(result));
                }
            }
        }

        /// <summary>
        /// Called before client calls are sent and after service responses are returned.
        /// </summary>
        /// <param name="operationName">The name of the operation.</param>
        /// <param name="inputs">The objects passed to the method by the client.</param>
        /// <returns>
        /// The correlation state that is returned as the <paramref name="correlationState" /> parameter in <see cref="M:System.ServiceModel.Dispatcher.IParameterInspector.AfterCall(System.String,System.Object[],System.Object,System.Object)" />. Return null if you do not intend to use correlation state.
        /// </returns>
        public object BeforeCall(string operationName, object[] inputs)
        {
            return null;
        }

        #endregion

        #region IEndpointBehavior

        /// <summary>
        /// Implement to pass data at runtime to bindings to support custom behavior.
        /// </summary>
        /// <param name="endpoint">The endpoint to modify.</param>
        /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {

        }

        /// <summary>
        /// Implements a modification or extension of the client across an endpoint.
        /// </summary>
        /// <param name="endpoint">The endpoint that is to be customized.</param>
        /// <param name="clientRuntime">The client runtime to be customized.</param>
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {   
            foreach (ClientOperation clientOperation in clientRuntime.ClientOperations)
                clientOperation.ClientParameterInspectors.Add(this);
        }

        /// <summary>
        /// Implements a modification or extension of the service across an endpoint.
        /// </summary>
        /// <param name="endpoint">The endpoint that exposes the contract.</param>
        /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {

        }

        /// <summary>
        /// Implement to confirm that the endpoint meets some intended criteria.
        /// </summary>
        /// <param name="endpoint">The endpoint to validate.</param>
        public void Validate(ServiceEndpoint endpoint)
        {

        }

        #endregion
    }
}
